package com.dure.fqllib;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;

public class RSAUtil {

    private static final int ENCRPT_MAX_LEN = 117;

    private static final int DECRPT_MAX_LEN = 128;

    /**
     * @param cipher  cipher
     * @param content content
     * @param maxLen  maxLen
     * @return ""
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    private static ByteArrayOutputStream doFinal(Cipher cipher, byte[] content, int maxLen) throws IllegalBlockSizeException,
            BadPaddingException, IOException {
        InputStream ins = new ByteArrayInputStream(content);
        ByteArrayOutputStream writer = new ByteArrayOutputStream();
        byte[] buf = new byte[maxLen];
        int bufl;
        while ((bufl = ins.read(buf)) != -1) {
            byte[] block;
            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];
                System.arraycopy(buf, 0, block, 0, bufl);
            }
            byte[] eData = cipher.doFinal(block);
            writer.write(eData);
        }
        return writer;
    }

    /**
     * 得到私钥
     *
     * @param priKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(String priKey) throws Exception {
        byte[] priKeyBytes = Base64.decode(priKey);
        PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(priKeySpec);
    }

    /**
     * 得到公钥
     *
     * @param pubKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    private static PublicKey getPublicKey(String pubKey) throws Exception {
        byte[] pubkeyBytes = Base64.decode(pubKey);
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubkeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(pubKeySpec);
    }

    /**
     * 私钥签名
     *
     * @param content    原文
     * @param privateKey Base64私钥
     * @return HexString
     * @throws Exception
     */
    public static String privateSignToHexString(String content, String privateKey) throws Exception {
        byte[] srcData = SHA1.getDigestOfBytes(content.getBytes("UTF-8"));
        System.out.println("SHA1:" + DataConvert.ByteArraytoHexString(srcData));
        byte[] eData = privateEncrypt(srcData, privateKey);
        return DataConvert.ByteArraytoHexString(eData);
    }


    /**
     * 公钥签名
     *
     * @param content   原文
     * @param publicKey Base64公钥
     * @return HexString
     * @throws Exception
     */
    public static String publicSignToHexString(String content, String publicKey) throws Exception {
        byte[] srcData = SHA1.getDigestOfBytes(content.getBytes("UTF-8"));
        System.out.println("SHA1:" + DataConvert.ByteArraytoHexString(srcData));
        byte[] eData = publicEncrypt(srcData, publicKey);
        return DataConvert.ByteArraytoHexString(eData);
    }


    public static byte[] publicEncrypt(byte[] content, String publicKey) throws Exception {
        PublicKey pubKey = getPublicKey(publicKey);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        ByteArrayOutputStream out = doFinal(cipher, content, ENCRPT_MAX_LEN);
        return out.toByteArray();
    }

    /**
     * 公钥验签(解拆验证签名流程 与 自动验签结果一致)
     *
     * @param content   原文
     * @param hexSign   16进制签名值
     * @param publicKey Base64公钥
     * @return boolean
     * @throws Exception
     */
    public static boolean publicDecryptVerifySign(String content, String hexSign, String publicKey) throws Exception {
        String sha1Data = SHA1.getDigestOfString(content.getBytes("UTF-8"));
        byte[] dData = publicDecrypt(DataConvert.hex2byte(hexSign), publicKey);
        String sign = DataConvert.ByteArraytoHexString(dData);
        if (sign.length() > sha1Data.length()) {
            sign = sign.substring(sign.length() - sha1Data.length());
        }
        return sha1Data.equalsIgnoreCase(sign);
    }

    /**
     * 私钥验证签名
     *
     * @param content    原文
     * @param hexSign    16进制签名值
     * @param privateKey Base64私钥
     * @return boolean
     * @throws Exception
     */
    public static boolean privateVerifySign(String content, String hexSign, String privateKey) throws Exception {
        String sha1Data = SHA1.getDigestOfString(content.getBytes("UTF-8"));
        byte[] dData = privateDecrypt(DataConvert.hex2byte(hexSign), privateKey);
        String sign = DataConvert.ByteArraytoHexString(dData);
        if (sign.length() > sha1Data.length()) {
            sign = sign.substring(sign.length() - sha1Data.length());
        }
        return sha1Data.equalsIgnoreCase(sign);
    }

    public static byte[] privateDecrypt(byte[] content, String privateKey) throws Exception {
        PrivateKey priKey = getPrivateKey(privateKey);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        ByteArrayOutputStream out = doFinal(cipher, content, DECRPT_MAX_LEN);
        return out.toByteArray();
    }

    public static byte[] publicDecrypt(byte[] content, String publicKey) throws Exception {
        PublicKey pubKey = getPublicKey(publicKey);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, pubKey);
        ByteArrayOutputStream out = doFinal(cipher, content, DECRPT_MAX_LEN);
        return out.toByteArray();
    }

    public static byte[] privateEncrypt(byte[] content, String privateKey) throws Exception {
        PrivateKey priKey = getPrivateKey(privateKey);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, priKey);
        ByteArrayOutputStream out = doFinal(cipher, content, ENCRPT_MAX_LEN);
        return out.toByteArray();
    }
}
